Repository navigation
feat: add a Paid column option to the Spend reports table - #103956
Conversation
Add an optional Paid date column for expense reports. The date comes from the latest payment action that was not cancelled, since the Search response has no paid date on the report. Share that rule with the Paid by column.
|
All contributors have signed the CLA ✍️ ✅ |
|
I have read the CLA Document and I hereby sign the CLA |
|
@Expensify/design adding an optional Paid column to the Spend > Reports table, matching the existing Approved and First approved columns: same header style and date format, and the same fixed width as First approved. It's off by default and enabled from Display > Edit columns. Videos are in the description. Anything you'd like changed? |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ab766b70bf
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Paid had a fixed wide width, so it didn't line up with Approved. It now uses the narrow width like the other date columns and widens only when a paid date is from a past year and needs to show the year.
|
Good catch @shawnborton. |
|
Great, thank you! |
|
Looking very good! |
|
@FitseTLT this is ready for review. The HOLD is only for merging, until the backend change from #103650 (comment) is live. Design has already reviewed the UI changes. |
|
@FitseTLT the backend change is live, so this is off hold and ready for review. Videos are updated, including sorting by Paid. |
garrettmknight
left a comment
There was a problem hiding this comment.
Looks good from a product perspective.
|
You're theory doesn't work for me I added paid in Reports in web but it is not available in mobile. 2026-10-23.18-45-35.mp4 |
|
Okay it works after signing out/in but the theory applies for Expenses tab too. So you should update the test steps accordingly. |
Reviewer Checklist
Screenshots/VideosAndroid: HybridApp2026-10-26.22-50-56.mp4Android: mWeb Chrome2026-10-26.22-37-30.mp4iOS: HybridAppiOS: mWeb Safari2026-10-26.22-36-05.mp4MacOS: Chrome / Safari2026-10-23.19-54-56.mp4 |
|
@JS00001 the sortBy column options for mobile is totally random. there is a theory by @sam5235 that the columns open in desktop appears if sign out and in for mobile but that's not consistent from my testing. In my testing IOS web aligned with the theory but for android and android web it didn't there is no way it would be platform specific I think there is some problem in BE because there is nothing responsible for that in FE. So we can investigate it separately but I don't think we should hold this pr for it. WDYT |
I agree with this one. Upon further investigation in the apps with the same account, my earlier explanation doesn't capture the full picture of how Sort by behaves on mobile:
This is the same on It might be worth agreeing on a clearer, predictable rule for when a column and Sort by selection made in one session is saved beyond the local cache and picked up by the user's other sessions (on sign-in, cache clear, or switching tabs). That would make this behavior easier to rely on in implementation and testing. |
👍🏻 Thanks, I have updated the test steps in the PR description. |
|
We intentionally do not live-sync app and web columns/filters. These will only reflect one another when the cache is cleared on the FE |
|
conflicts @sam5235 |
Resolving it |
…olumn # Conflicts: # tests/unit/Search/SearchUIUtilsTest.ts
|
@FitseTLT , resolved the conflict ✅ . |
garrettmknight
left a comment
There was a problem hiding this comment.
Looks good from a product perspective.
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
🚧 JS00001 has triggered a test Expensify/App build. You can view the workflow run here. |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/JS00001 in version: 9.5.24-1 🚀
|
|
Yes, a help site update is needed. This PR adds a new user-facing column, Paid, to Spend > Reports and Spend > Expenses, and it isn't behind a beta. I created a draft PR: Docs updates for E/A#103956 Add Paid column to Spend column list It adds one line to the column list in Using-Reports-in-New-Expensify.md, next to Submitted:
I checked the Paid label in the live UI under Display > Edit columns on both tables. I applied the @sam5235, please review the linked help site PR and confirm it reflects the current behavior. Then mark the linked help site PR |
Reviewed the help site PR #105190. The new line is accurate: the Paid column shows the date the report was paid, on both Spend > Reports and Spend > Expenses, and it stays blank until the report is paid. I don't have permission to mark it ready for review, so @JS00001 could you mark it ready when you get a chance? |
|
Deploy Blocker #105276 was identified to be related to this PR. |
|
🚀 Deployed to production by https://github.com/jasperhuangg in version: 9.5.24-8 🚀
Bundle Size Analysis (Sentry): |

Explanation of Change
The Spend tables can show when a report was submitted and approved, but not when it was paid. This adds an optional Paid column to the Reports table and the Expenses table.
TABLE_COLUMNS.PAIDis added to the expense report columns after First approved, and to the expense columns after Approved (COLUMN_AVAILABILITY, Search only). It is off by default and uses the existing "Paid" label (search.filters.paid).getPaidDateuses the report'spaiddate from the Search response, or the latest payment action's date if that is newer. The action covers paying from Search (including offline), before the next search refreshesreport.paid. It is blank unless the report status is Paid, and a payment that was later cancelled, dequeued or bounced does not count. A payment the submitter marked as received does count, because the report was still paid on that date.paidis added to theReporttype and registered inwhitelistedReportKeys,DebugUtilsandgetStableReportSelector, next toapproved(same asdebitedAmountin [Payment due @huult] [NO QA] Add Amount debited and Amount reimbursed filters, columns and sorting #97708, also a Search-only report field).getLastPaidAction(Paid by) now live ingetPaymentAction(latest or earliest valid payment, ignoring cancelled ones) andisPaymentMarkedAsReceived, used by both Paid and Paid by. Paid by's behaviour is unchanged and covered by its existing tests. Paid takes the earliest "received" marking, matching the backend, and the latest real payment.paiddate is from a past year (shouldShowYearPaid, read fromreport.paidlike Approved readsreport.approved).getTableMinWidthcounts it as a date column.expenseReportColumnNamesToSortingPropertyandtransactionColumnNamesToSortingProperty, so the header is sortable and Paid shows in "Sort by" when the column is enabled.Backend change (details) is live on staging and production. Search returns
paidon each report when the query asks for it (the Paid column is selected, or sorting by Paid) and acceptssortBy:paid, for bothtype:expense-reportandtype:expense.Areas that could be affected: the Paid by column (shared helper), the Reports and Expenses table headers and rows on wide layouts, and the Sort by list.
Unrelated console error: while testing I saw
The result of getSnapshot should be cached to avoid an infinite loopfromReportActionsListItemRendereron web and iOS. It also appears on the sign-in screen before any report code runs, and this PR doesn't add or change any store subscriptions, so it isn't caused by this change. Happy to open a separate issue for it.Unrelated bug: if a report is approved and then paid in the same session, its Approved date goes blank until reload. It happens on main too:
getApprovedDateonly uses the APPROVED action while the status is Approved, and approving doesn't setreport.approvedoptimistically.Fixed Issues
$ #103650
PROPOSAL: #103650 (comment)
Tests
Precondition: an account that is admin of a workspace, with paid expense reports paid on at least two different days, and one approved expense report that has not been paid yet.
Reports table
Expenses table
Narrow layout
Edit columns is only available on wide layouts, so mobile uses columns from a saved search created on web.
Offline tests
QA Steps
Same as tests.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android.Native.mp4
e5e9e-448d-4fff-920d-07494ce01649
Android: mWeb Chrome
Android.mWeb.mp4
iOS: Native
IOS.Native.mp4
iOS: mWeb Safari
IOS.mWeb.Safari.mp4
MacOS: Chrome / Safari
MacOS.Chrome.mov